﻿window.onload = function() {
	var paper = Raphael("holder");

	// var curve = paper.ellipse(100, 100, 1, 1).attr({"stroke-width": 0, fill:
	// Color.red});

	var text = "Betty Botter bought some butter but, she said, the butter's bitter. If I put it in my batter, it will make my batter bitter. But a bit of better butter will make my batter better. So, she bought a bit of butter, better than her bitter butter, and she put it in her batter, and the batter was not bitter. It was better Betty Botter bought a bit better butter.";
	var font = {
		font : "11px Arial",
		"font-style" : "italic",
		opacity : 1,
		"fill" : LABEL_COLOR,
		stroke : LABEL_COLOR,
		"stroke-width" : .3
	};
	var font = {
		font : "11px Arial",
		opacity : 1,
		"fill" : LABEL_COLOR
	};
	var boxWidth = 100

	var AttributedStringIterator = function(text) {
		// this.text = this.rtrim(this.ltrim(text));
		text = text.replace(/(\s)+/, " ");
		this.text = this.rtrim(text);
		/*
		 * if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
		 * throw new IllegalArgumentException("Invalid substring range"); }
		 */
		this.beginIndex = 0;
		this.endIndex = this.text.length;
		this.currentIndex = this.beginIndex;

		// console.group("[AttributedStringIterator]");
		var i = 0;
		var string = this.text;
		var fullPos = 0;

		// console.log("string: \"" + string + "\", length: " + string.length);
		this.startWordOffsets = [];
		this.startWordOffsets.push(fullPos);

		// TODO: remove i 1000
		while (i < 1000) {
			var pos = string.search(/[ \t\n\f-\.\,]/);
			if (pos == -1)
				break;

			// whitespace start
			fullPos += pos;
			string = string.substr(pos);
			// //console.log("fullPos: " + fullPos + ", pos: " + pos + ",
			// string: ", string);

			// remove whitespaces
			var pos = string.search(/[^ \t\n\f-\.\,]/);
			if (pos == -1)
				break;

			// whitespace end
			fullPos += pos;
			string = string.substr(pos);

			// //console.log("fullPos: " + fullPos);
			this.startWordOffsets.push(fullPos);

			i++;
		}
		// console.log("startWordOffsets: ", this.startWordOffsets);
		// console.groupEnd();
	};
	AttributedStringIterator.prototype = {
		getEndIndex : function(pos) {
			if (typeof(pos) == "undefined")
				return this.endIndex;

			var string = this.text.substr(pos, this.endIndex - pos);

			var posEndOfLine = string.search(/[\n]/);
			if (posEndOfLine == -1)
				return this.endIndex;
			else
				return pos + posEndOfLine;
		},
		getBeginIndex : function() {
			return this.beginIndex;
		},
		isWhitespace : function(pos) {
			var str = this.text[pos];
			var whitespaceChars = " \t\n\f";

			return (whitespaceChars.indexOf(str) != -1);
		},
		isNewLine : function(pos) {
			var str = this.text[pos];
			var whitespaceChars = "\n";

			return (whitespaceChars.indexOf(str) != -1);
		},
		preceding : function(pos) {
			// console.group("[AttributedStringIterator.preceding]");
			for (var i in this.startWordOffsets) {
				var startWordOffset = this.startWordOffsets[i];
				if (pos < startWordOffset && i > 0) {
					// console.log("startWordOffset: " +
					// this.startWordOffsets[i-1]);
					// console.groupEnd();
					return this.startWordOffsets[i - 1];
				}
			}
			// console.log("pos: " + pos);
			// console.groupEnd();
			return this.startWordOffsets[i];
		},
		following : function(pos) {
			// console.group("[AttributedStringIterator.following]");
			for (var i in this.startWordOffsets) {
				var startWordOffset = this.startWordOffsets[i];
				if (pos < startWordOffset && i > 0) {
					// console.log("startWordOffset: " +
					// this.startWordOffsets[i]);
					// console.groupEnd();
					return this.startWordOffsets[i];
				}
			}
			// console.log("pos: " + pos);
			// console.groupEnd();
			return this.startWordOffsets[i];
		},
		ltrim : function(str) {
			var patt2 = /^\s+/g;
			return str.replace(patt2, "");
		},
		rtrim : function(str) {
			var patt2 = /\s+$/g;
			return str.replace(patt2, "");
		},
		getLayout : function(start, limit) {
			return this.text.substr(start, limit - start);
		},
		getCharAtPos : function(pos) {
			return this.text[pos];
		}
	};

	/*
	 * var TextMeasurer = function(paper, text, fontAttrs){ this.text = text;
	 * this.paper = paper; this.fontAttrs = fontAttrs;
	 * 
	 * this.fStart = this.text.getBeginIndex();
	 *  }; TextMeasurer.prototype = { getLineBreakIndex: function(start,
	 * maxAdvance){ var localStart = start - this.fStart; }, getLayout:
	 * function(){ } }
	 */

	var LineBreakMeasurer = function(paper, text, fontAttrs) {
		this.paper = paper;
		this.text = new AttributedStringIterator(text);
		this.fontAttrs = fontAttrs;

		if (this.text.getEndIndex() - this.text.getBeginIndex() < 1) {
			throw {
				message : "Text must contain at least one character.",
				code : "IllegalArgumentException"
			};
		}

		// this.measurer = new TextMeasurer(paper, this.text, this.fontAttrs);
		this.limit = this.text.getEndIndex();
		this.pos = this.start = this.text.getBeginIndex();

		this.rafaelTextObject = this.paper.text(100, 100, this.text.text)
				.attr(fontAttrs).attr("text-anchor", "start");
		this.svgTextObject = this.rafaelTextObject[0];
	};
	LineBreakMeasurer.prototype = {
		nextOffset : function(wrappingWidth, offsetLimit, requireNextWord) {
			// console.group("[nextOffset]");
			var nextOffset = this.pos;
			if (this.pos < this.limit) {
				if (offsetLimit <= this.pos) {
					throw {
						message : "offsetLimit must be after current position",
						code : "IllegalArgumentException"
					};
				}

				var charAtMaxAdvance = this.getLineBreakIndex(this.pos,
						wrappingWidth);
				// charAtMaxAdvance --;
				// console.log("charAtMaxAdvance:", charAtMaxAdvance, ", [" +
				// this.text.getCharAtPos(charAtMaxAdvance) + "]");

				if (charAtMaxAdvance == this.limit) {
					nextOffset = this.limit;
					// console.log("charAtMaxAdvance == this.limit");
				} else if (this.text.isNewLine(charAtMaxAdvance)) {
					console.log("isNewLine");
					nextOffset = charAtMaxAdvance + 1;
				} else if (this.text.isWhitespace(charAtMaxAdvance)) {
					// TODO: find next noSpaceChar
					// return nextOffset;
					nextOffset = this.text.following(charAtMaxAdvance);
				} else {
					// Break is in a word; back up to previous break.
					/*
					 * var testPos = charAtMaxAdvance + 1; if (testPos ==
					 * this.limit) { console.error("hbz..."); } else {
					 * nextOffset = this.text.preceding(charAtMaxAdvance); }
					 */
					nextOffset = this.text.preceding(charAtMaxAdvance);

					if (nextOffset <= this.pos) {
						nextOffset = Math.max(this.pos + 1, charAtMaxAdvance);
					}
				}
			}
			if (nextOffset > offsetLimit) {
				nextOffset = offsetLimit;
			}
			// console.log("nextOffset: " + nextOffset);
			// console.groupEnd();
			return nextOffset;
		},
		nextLayout : function(wrappingWidth) {
			// console.groupCollapsed("[nextLayout]");
			if (this.pos < this.limit) {
				var requireNextWord = false;
				var layoutLimit = this.nextOffset(wrappingWidth, this.limit,
						requireNextWord);
				// console.log("layoutLimit:", layoutLimit);
				if (layoutLimit == this.pos) {
					// console.groupEnd();
					return null;
				}
				var result = this.text.getLayout(this.pos, layoutLimit);
				// console.log("layout: \"" + result + "\"");

				// remove end of line

				// var posEndOfLine = this.text.getEndIndex(this.pos);
				// if (posEndOfLine < result.length)
				// result = result.substr(0, posEndOfLine);

				this.pos = layoutLimit;

				// console.groupEnd();
				return result;
			} else {
				// console.groupEnd();
				return null;
			}
		},
		getLineBreakIndex : function(pos, wrappingWidth) {
			// console.group("[getLineBreakIndex]");
			// console.log("pos:"+pos + ", text: \""+
			// this.text.text.replace(/\n/g, "_").substr(pos, 1) + "\"");

			var bb = this.rafaelTextObject.getBBox();

			var charNum = -1;
			try {
				var svgPoint = this.svgTextObject.getStartPositionOfChar(pos);
				// var dot = this.paper.ellipse(svgPoint.x, svgPoint.y, 1,
				// 1).attr({"stroke-width": 0, fill: Color.blue});
				svgPoint.x = svgPoint.x + wrappingWidth;
				// svgPoint.y = bb.y;
				// console.log("svgPoint:", svgPoint);

				// var dot = this.paper.ellipse(svgPoint.x, svgPoint.y, 1,
				// 1).attr({"stroke-width": 0, fill: Color.red});

				charNum = this.svgTextObject.getCharNumAtPosition(svgPoint);
			} catch (e) {
				console.warn("getStartPositionOfChar error, pos:" + pos);
				/*
				 * var testPos = pos + 1; if (testPos < this.limit) { return
				 * testPos }
				 */
			}
			// console.log("charNum:", charNum);
			if (charNum == -1) {
				// console.groupEnd();
				return this.text.getEndIndex(pos);
			} else {
				// When case there is new line between pos and charnum then use
				// this new line
				var newLineIndex = this.text.getEndIndex(pos);
				if (newLineIndex < charNum) {
					console.log("newLineIndex <= charNum, newLineIndex:"
									+ newLineIndex + ", charNum:" + charNum,
							"\""
									+ this.text.text.substr(newLineIndex + 1)
											.replace(/\n/g, "↵") + "\"");
					// console.groupEnd();

					return newLineIndex;
				}

				// var charAtMaxAdvance = this.text.text.substring(charNum,
				// charNum + 1);
				var charAtMaxAdvance = this.text.getCharAtPos(charNum);
				// console.log("!!charAtMaxAdvance: " + charAtMaxAdvance);
				// console.groupEnd();
				return charNum;
			}
		},
		getPosition : function() {
			return this.pos;
		}
	};

	// ******
	function drawMultilineText(text, x, y, boxWidth, boxHeight, options) {
		var TEXT_PADDING = 3;
		var width = boxWidth - (2 * TEXT_PADDING);
		if (boxHeight)
			var height = boxHeight - (2 * TEXT_PADDING);

		var layouts = [];

		var measurer = new LineBreakMeasurer(paper, text, font);
		var lineHeight = measurer.rafaelTextObject.getBBox().height;
		console.log("text: ", text.replace(/\n/g, "↵"));

		if (height) {
			var availableLinesCount = parseInt(height / lineHeight);
			console.log("availableLinesCount: " + availableLinesCount);
		}

		var i = 1;
		while (measurer.getPosition() < measurer.text.getEndIndex()) {
			var layout = measurer.nextLayout(width);
			// console.log("LAYOUT: " + layout + ", getPosition: " +
			// measurer.getPosition());

			if (layout != null) {
				if (!availableLinesCount || i < availableLinesCount) {
					layouts.push(layout);
				} else {
					layouts.push(fitTextToWidth(layout + "...", boxWidth));
					break;
				}
			}
			i++;
		};
		console.log(layouts);

		measurer.rafaelTextObject.attr({
					"text" : layouts.join("\n")
				});
		// measurer.rafaelTextObject.attr({"text-anchor": "end"});
		// measurer.rafaelTextObject.attr({"text-anchor": "middle"});
		if (options)
			measurer.rafaelTextObject.attr({
						"text-anchor" : options["text-anchor"]
					});

		var bb = measurer.rafaelTextObject.getBBox();
		// measurer.rafaelTextObject.attr({"x": x + boxWidth/2});
		if (options["vertical-align"] == "top")
			measurer.rafaelTextObject.attr({
						"y" : y + bb.height / 2 + TEXT_PADDING
					});
		else
			measurer.rafaelTextObject.attr({
						"y" : y + height / 2
					});
		// var bb = measurer.rafaelTextObject.getBBox();

		if (measurer.rafaelTextObject.attr("text-anchor") == "middle")
			measurer.rafaelTextObject.attr("x", x + boxWidth / 2 + TEXT_PADDING
							/ 2);
		else if (measurer.rafaelTextObject.attr("text-anchor") == "end")
			measurer.rafaelTextObject
					.attr("x", x + boxWidth + TEXT_PADDING / 2);
		else
			measurer.rafaelTextObject.attr("x", x + boxWidth / 2 - bb.width / 2
							+ TEXT_PADDING / 2);

		var boxStyle = {
			stroke : Color.LightSteelBlue2,
			"stroke-width" : 1.0,
			"stroke-dasharray" : "- "
		};
		/*
		 * var box = paper.rect(x+.0 + boxWidth/2 - bb.width/2+ TEXT_PADDING/2,
		 * y + .5 + boxHeight/2 - bb.height/2, width, height).attr(boxStyle);
		 * box.attr("height", bb.height);
		 */
		// var box = paper.rect(bb.x - .5 + bb.width/2 + TEXT_PADDING, bb.y +
		// bb.height/2, bb.width, bb.height).attr(boxStyle);
		var textAreaCX = x + boxWidth / 2;
		var textAreaCY = y + height / 2;
		var dotLeftTop = paper.ellipse(x, y, 3, 3).attr({
					"stroke-width" : 0,
					fill : Color.LightSteelBlue,
					stroke : "none"
				});
		var dotCenter = paper.ellipse(textAreaCX, textAreaCY, 3, 3).attr({
					fill : Color.LightSteelBlue2,
					stroke : "none"
				});

		/*
		 * // real bbox var bb = measurer.rafaelTextObject.getBBox(); var rect =
		 * paper.rect(bb.x+.5, bb.y + .5, bb.width,
		 * bb.height).attr({"stroke-width": 1});
		 */
		var boxStyle = {
			stroke : Color.LightSteelBlue2,
			"stroke-width" : 1.0,
			"stroke-dasharray" : "- "
		};
		var rect = paper.rect(x + .5, y + .5, boxWidth, boxHeight)
				.attr(boxStyle);
	}

	/*
	 * for (var i=0; i<1; i++) { var t = text; //var t = "Высококвалифицирова";
	 * 
	 * var text = paper.text(300, 100, t).attr(font).attr("text-anchor",
	 * "start"); var bbText = text.getBBox(); paper.rect(300+.5, 100 + .5,
	 * bbText.width, bbText.height).attr({"stroke-width": 1}); console.log("t: ",
	 * t.replace(/\n/g, "↵"));
	 * 
	 * while (measurer.getPosition() < measurer.text.getEndIndex()) { var layout =
	 * measurer.nextLayout(width); //console.log("LAYOUT: " + layout + ",
	 * getPosition: " + measurer.getPosition()); if (layout != null)
	 * layouts.push(layout); };
	 * 
	 * measurer.rafaelTextObject.attr("text", layouts.join("\n")); var bb =
	 * measurer.rafaelTextObject.getBBox(); var rect = paper.rect(bb.x+.5, bb.y +
	 * .5, bb.width, bb.height).attr({"stroke-width": 1});
	 * 
	 * lay.push(layouts); console.log(layouts); }
	 */

	var fitTextToWidth = function(original, width) {
		var text = original;

		// TODO: move attr on parameters
		var attr = {
			font : "11px Arial",
			opacity : 0
		};

		// remove length for "..."
		var dots = paper.text(0, 0, "...").attr(attr).hide();
		var dotsBB = dots.getBBox();

		var maxWidth = width - dotsBB.width;

		var textElement = paper.text(0, 0, text).attr(attr).hide();
		var bb = textElement.getBBox();

		// it's a little bit incorrect with "..."
		while (bb.width > maxWidth && text.length > 0) {
			text = text.substring(0, text.length - 1);
			textElement.attr({
						"text" : text
					});
			bb = textElement.getBBox();
		}

		// remove element from paper
		textElement.remove();

		if (text != original) {
			text = text + "...";
		}

		return text;
	}

	var x = 100, y = 90, height = 20;
	var options = {
		"text-anchor" : "middle",
		"boxHeight" : 150,
		"vertical-align" : "top"
	};
	var options = {
		"boxHeight" : 150,
		"vertical-align" : "top"
	};
	drawMultilineText(text, x, y, 150, 100, options);
};